# ~*~ coding: utf-8 ~*~
import time
from functools import reduce

from apps.common.dbbase import BaseModel
from apps.common.utils import generate_uuid
from apps.extensions import db
from .user import SystemUser
from .asset import Asset
from ..tasks import push_system_users_to_assets, delete_system_users_to_assets

__all__ = ['AssetPermission', 'AssetPermissionRelated']


class ActionMixin:
    NONE = 0
    CONNECT = 1
    UPLOAD = 2
    DOWNLOAD = 4
    ALL = 7

    NAME_MAP = {
        CONNECT: "connect",
        UPLOAD: "upload_file",
        DOWNLOAD: "download_file",
    }

    NAME_MAP_REVERSE = dict({v: k for k, v in NAME_MAP.items()})

    # 字段
    actions = db.Column(db.INT, default=ALL)

    @classmethod
    def choices_to_value(cls, value):
        """
        将传入的权限计算成权限值
        :param value: ["connect",  "upload_file", "upload_file"]
        :return: 7
        """
        if not isinstance(value, list):
            return cls.NONE
        db_value = [
            cls.NAME_MAP_REVERSE[v] for v in value
            if v in cls.NAME_MAP_REVERSE.keys()
        ]
        if not db_value:
            return cls.NONE

        def to_choices(x, y):
            return x | y

        result = reduce(to_choices, db_value)
        return result

    @classmethod
    def value_to_choices(cls, value):
        """
        将传入成权限值计算成权限
        :param value: 7
        :return: ["connect",  "upload_file", "upload_file"]
        """
        value = int(value)
        choices = [cls.NAME_MAP[i] for i, j in cls.NAME_MAP.items() if value & i == i]
        return choices

    def check_action_perm(self, value):
        """
        计算用户是否有该权限
        :param value: "upload_file"
        :return:
        """
        if self.actions == self.NONE:
            return False

        try:
            value = self.NAME_MAP_REVERSE[value]
        except KeyError:
            return False
        return self.actions & value == value


class AssetPermission(ActionMixin, BaseModel):
    __tablename__ = 'assets_permission'

    id = db.Column(db.CHAR(36), default=generate_uuid, primary_key=True)
    name = db.Column(db.VARCHAR(128), unique=True)
    is_active = db.Column(db.Boolean, default=True)

    create_by = db.Column(db.VARCHAR(32), nullable=True)
    create_time = db.Column(db.INT, default=time.time)
    update_time = db.Column(db.INT, default=time.time, onupdate=time.time)
    comment = db.Column(db.TEXT, nullable=True, default='')

    def create_related(self, user_ids, system_user_ids, asset_ids):
        asset_perm_related_list = []
        system_users = []
        assets = []
        for system_user_id in system_user_ids:
            for asset_id in asset_ids:
                is_existed = db.session.query(AssetPermissionRelated). \
                    filter(
                    AssetPermissionRelated.asset_id == asset_id,
                    AssetPermissionRelated.system_user_id == system_user_id,
                ).count()

                # 该资产是否存在该系统用户
                if not is_existed:
                    system_user = db.session.query(SystemUser).get(system_user_id)
                    asset = db.session.query(Asset).get(asset_id)
                    assets.append(asset)
                    system_users.append(system_user)

                for user_id in user_ids:
                    kwargs = {
                        'id': generate_uuid(),
                        'user_id': user_id,
                        'asset_id': asset_id,
                        'system_user_id': system_user_id,
                        'asset_permission_id': self.id,
                    }
                    apr = AssetPermissionRelated(**kwargs)
                    asset_perm_related_list.append(apr)

        db.session.add_all(asset_perm_related_list)
        push_system_users_to_assets.apply_async(args=(system_users, assets), serializer='pickle')

    def delete_related(self):
        need_delete_related = db.session.query(AssetPermissionRelated). \
            filter(AssetPermissionRelated.asset_permission_id == self.id). \
            all()

        db.session.query(AssetPermissionRelated). \
            filter(AssetPermissionRelated.id.in_([related.id for related in need_delete_related])). \
            delete(synchronize_session=False)

        system_users = []
        assets = []
        for related in need_delete_related:
            is_existed = db.session.query(AssetPermissionRelated). \
                filter(
                AssetPermissionRelated.asset_id == related.asset_id,
                AssetPermissionRelated.system_user_id == related.system_user_id,
            ).count()
            if not is_existed:
                system_user = db.session.query(SystemUser).get(related.system_user_id)
                asset = db.session.query(Asset).get(related.asset_id)
                system_users.append(system_user)
                assets.append(asset)
        delete_system_users_to_assets.apply_async(args=(system_users, assets), serializer='pickle')

    def update_related(self, user_ids, system_user_ids, asset_ids):
        asset_permission_related_old_list = db.session.query(AssetPermissionRelated). \
            filter_by(asset_permission_id=self.id). \
            all()
        asset_permission_related_new_list = []
        for system_user_id in system_user_ids:
            for asset_id in asset_ids:
                for user_id in user_ids:
                    kwargs = {
                        'user_id': user_id,
                        'asset_id': asset_id,
                        'system_user_id': system_user_id,
                        'asset_permission_id': self.id,
                    }
                    asset_permission_related_new_list.append(AssetPermissionRelated(**kwargs))

        need_create_related = []
        for new in asset_permission_related_new_list:
            if new not in asset_permission_related_old_list:
                need_create_related.append(new)

        need_delete_related = []
        for old in asset_permission_related_old_list:
            if old not in asset_permission_related_new_list:
                need_delete_related.append(old)

        # create by ansible
        create_system_users = []
        create_assets = []
        for related in need_create_related:
            is_existed = db.session.query(AssetPermissionRelated).filter(
                AssetPermissionRelated.asset_id == related.asset_id,
                AssetPermissionRelated.system_user_id == related.system_user_id,
            ).count()
            if not is_existed:
                system_user = db.session.query(SystemUser).get(related.system_user_id)
                asset = db.session.query(Asset).get(related.asset_id)
                create_system_users.append(system_user)
                create_assets.append(asset)
        push_system_users_to_assets.apply_async(
            args=(set(create_system_users), set(create_assets)),
            serializer='pickle'
        )

        # create by database
        db.session.add_all(need_create_related)

        # delete by ansible
        delete_system_users = []
        delete_assets = []

        for related in need_delete_related:
            is_existed = db.session.query(AssetPermissionRelated).filter(
                AssetPermissionRelated.asset_id == related.asset_id,
                AssetPermissionRelated.system_user_id == related.system_user_id,
            ).count()
            if is_existed:
                system_user = db.session.query(SystemUser).get(related.system_user_id)
                asset = db.session.query(Asset).get(related.asset_id)
                delete_system_users.append(system_user)
                delete_assets.append(asset)
        delete_system_users_to_assets.apply_async(
            args=(set(delete_system_users), set(delete_assets)),
            serializer='pickle'
        )

        # delete by database
        db.session.query(AssetPermissionRelated). \
            filter(AssetPermissionRelated.id.in_([related.id for related in need_delete_related])). \
            delete(synchronize_session=False)

    def __str__(self):
        return "<AssetPermission: {}>".format(self.name)


class AssetPermissionRelated(BaseModel):
    __tablename__ = 'assets_permission_related'

    id = db.Column(db.CHAR(36), default=generate_uuid, primary_key=True)
    user_id = db.Column(db.CHAR(36))
    asset_id = db.Column(db.CHAR(36))
    system_user_id = db.Column(db.CHAR(36))
    asset_permission_id = db.Column(db.CHAR(36))

    def __str__(self):
        return "<AssetPermissionRelated: {} of {}>".format(self.asset_id, self.system_user_id)

    def __eq__(self, other):
        if not isinstance(other, self.__class__):
            return False

        fields_check = ['user_id', 'asset_id', 'system_user_id', 'asset_permission_id']
        for field in fields_check:
            if getattr(self, field) != getattr(other, field):
                return False
        return True







